package com.bestow.dox4j.component;

import com.bestow.dox4j.utils.Docx4jUtils;
import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.finders.RangeFinder;
import org.docx4j.model.fields.merge.DataFieldName;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.CTMarkupRange;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.P;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 书签替换文本插件，
 *
 * @author liliu
 * @date 2022-10-04
 */
@Component
public class TextBookmarkReplaceWithContents {

    protected static Logger log = LoggerFactory.getLogger(BookMarkReplaceComponent.class);

    private static boolean DELETE_BOOKMARK = true;

    /**
     * 把docx文件中的body的上下文和书签值（data中key为书签名，value为书签的值）传入方法中进行替换
     *
     * @param paragraphs (正文中的段落)
     * @param data
     */
    public void replaceBookmarkContents(List<Object> paragraphs, Map<DataFieldName, String> data, Map<DataFieldName, String> bookmarkType,
                                        Map<DataFieldName, String> dataFontFamily,
                                        Map<DataFieldName, String> dataFontSize,
                                        Map<DataFieldName, String> dataUnderLine,
                                        Map<DataFieldName, String> dataBold) {
        //当段落为空时,直接返回
        if (paragraphs == null || paragraphs.size() < 1) {
            return;
        }
        // 提取书签并创建书签的游标
        RangeFinder rt = new RangeFinder("CTBookmark" , "CTMarkupRange" );//CTBookmark表示一个书签标签的起点，CTMarkupRange表示一个书签标签的终点
        new TraversalUtil(paragraphs, rt);
        // 遍历书签
        for (CTBookmark bm : rt.getStarts()) {
            // 先判断书签的名字是否为空
            if (bm.getName() == null)
                continue;
            String value = data.get(new DataFieldName(bm.getName()));//根据书签名判断当前书签处是否有值，如果有则不处理，没有就进入下一步进行替换
            if (value == null || "NO_DEAL".equalsIgnoreCase(value)) continue;
            String bookmarkName = bm.getName();
            String bookMarkType = bookmarkType.get(new DataFieldName(bookmarkName));
            String fontFamily = dataFontFamily.get(new DataFieldName(bookmarkName));
            String fontSize = dataFontSize.get(new DataFieldName(bookmarkName));
            String underLine = dataUnderLine.get(new DataFieldName(bookmarkName));
            String bold = dataBold.get(new DataFieldName(bookmarkName));

            try {
                // Can't just remove the object from the parent,
                // since in the parent, it may be wrapped in a JAXBElement
                List<Object> theList = null;
                List<Object> theTopList = null;
                int currentPIndex = -1;
                P currentP = null;
                if (bm.getParent() instanceof P || bm.getParent() instanceof ContentAccessor) {
                    if (bm.getParent() instanceof P) {
                        currentP = (P) bm.getParent();
                        theList = ((ContentAccessor) currentP).getContent();
                        if (currentP.getParent() instanceof ContentAccessor) {
                            theTopList = ((ContentAccessor) currentP.getParent())
                                    .getContent();
                            currentPIndex = theTopList.indexOf(currentP);
                        }
                    } else {
                        theList = ((ContentAccessor) bm.getParent()).getContent();
                    }
                } else if (bm.getParent() instanceof ArrayList) {
                    theList = (ArrayList<Object>) bm.getParent();
                } else {
                    continue;
                }

                int rangeStart = -1;
                int rangeEnd = -1;
                int i = 0;
                for (Object ox : theList) {
                    Object listEntry = XmlUtils.unwrap(ox);
                    if (listEntry.equals(bm)) {
                        if (DELETE_BOOKMARK) {
                            rangeStart = i;
                        } else {
                            rangeStart = i + 1;
                        }
                    } else if (listEntry instanceof CTMarkupRange) {
                        if (((CTMarkupRange) listEntry).getId().equals(bm.getId())) {
                            if (DELETE_BOOKMARK) {
                                rangeEnd = i;
                            } else {
                                rangeEnd = i - 1;
                            }
                            break;
                        }
                    }
                    i++;
                }

                if (rangeStart >= 0 && rangeEnd > rangeStart) {//如果rangeStart位置为0则表示书签的起始位置在段落的起点上

                    // Delete the bookmark range
                    for (int j = rangeEnd; j >= rangeStart; j--) {
                        System.out.println("移除书签" );
                        theList.remove(j);
                    }


                    if ("TEXT".equals(bookMarkType) || "TEXT_AREA".equals(bookMarkType)) {
                        String[] contentArr = value.split("\n" );
                        if (currentP != null) {
                            org.docx4j.wml.R run = Docx4jUtils.makeRWithContent(contentArr[0], fontFamily, fontSize, underLine, bold);
                            theList.add(rangeStart, run);
                            for (int j = 1, len = contentArr.length; j < len; j++) {
                                run = Docx4jUtils.makeRWithContent(contentArr[j], fontFamily, fontSize, underLine, bold);
                                P paraP = new P();
                                paraP.getContent().add(run);
                                theList.add(rangeStart + j, paraP);
                            }
                        } else {
                            for (int j = 0, len = contentArr.length; j < len; j++) {
                                org.docx4j.wml.R run = Docx4jUtils.makeRWithContent(contentArr[j], fontFamily, fontSize, underLine, bold);
                                P paraP = new P();
                                paraP.getContent().add(run);
                                theList.add(rangeStart + j, paraP);
                            }
                        }
                    }
                }
            } catch (ClassCastException cce) {
                log.error(cce.getMessage(), cce);
            }
        }
    }

}
